#include "generate_expression.h"
#include "exceptions.h"

string generateExpression(ParseTree::Name ns, ParseTree::Expression &expr, ParseTree::Type typeHint)
{
    string c = "";
    
    // TODO cast if expression type is not typeHint
    
    if (expr.exprType == ParseTree::Expression::ExprOp)
    {
        c += generateOpExpr(ns, *expr.opExpr, typeHint);//+ generateActPars(ns, se.params);
    }
    
    if (expr.exprType == ParseTree::Expression::ExprAss)
    {
        c += generateAssignment(ns, *expr.assignment);
    }
    
    return c;
}

string generateAssignment(ParseTree::Name ns, ParseTree::Assignment &ass)
{
    // TODO: maybe typehint... see generateExpression()
    
    string c = "";

    Symbol s = getDesignatorSymbol(ns, ass.designator);
    if (s.type.typeType == ParseTree::Type::PrimitiveType)
    {
        //std::cout << "Generating assignment on " << printSymbol(s) << std::endl;
        //std::cout << "expr type: " << printType(getTypeOfExpression(ns, *ass.expression)) << std::endl;
        c = "*";
        c += generateDesignator(ns, ass.designator, s.type);
        c += " = *("; // TODO: other assignment operators
        c += generateExpression(ns, *ass.expression, s.type);
        c += ")";
    } else {
        c = "";
        c += generateDesignator(ns, ass.designator, s.type);
        c += " = "; // see above
        c += generateExpression(ns, *ass.expression, s.type);
    }
    
    return c;
}

vector<string> generateOpExpr2(ParseTree::Name ns, ParseTree::OpExpr &oe, ParseTree::Type typeHint)
{
    vector<string> tokens;
    if (oe.opExprType == ParseTree::OpExpr::Expr)
    {
        tokens.push_back(generateUnary(ns, *oe.unary, typeHint));
        
        // operator        
        ParseTree::Expression expression;
        expression.exprType = ParseTree::Expression::ExprOp;
        expression.opExpr = &oe;
        Symbol op = symbolTable[getDominantOperatorOverload(ns, expression)];
        //std::cout << "opexpr op: " << printSymbol(op) << std::endl;
        tokens.push_back(op.internal);
        
        vector<string> newTokens = generateOpExpr2(ns, *oe.next, typeHint);
        tokens.insert(tokens.end(), newTokens.begin(), newTokens.end());
    }
    else if (oe.opExprType == ParseTree::OpExpr::Single)
    {
        tokens.push_back(generateUnary(ns, *oe.unary, typeHint));
    }
    else if (oe.opExprType == ParseTree::OpExpr::Empty)
    {
        
    }
    
    return tokens;
}

void parenthesize(vector<string> &tokens, int i, bool non)
{
    string op = tokens[i];
    if (tokens[i - 1] != ")")
    {
        vector<string>::iterator it;
        it = tokens.begin() + (i - 1);
        tokens.insert(it, "(");
    } else {
        int depth = 1;
        int j = i - 2;
        while (depth > 0) // WTF, just a for loop, but in a while-disguise
        {
            if (tokens[j] == ")")
                ++depth;
            if (tokens[j] == "(")
                --depth;
            j--;
        } j++;
        vector<string>::iterator it;
        it = tokens.begin() + j;
        tokens.insert(it, "(");
    }
    
    i++;
    unsigned int lastInsert = 0;
    if (tokens[i + 1] != "(")
    {
        vector<string>::iterator it;
        it = tokens.begin() + (i + 2);
        tokens.insert(it, ")");
        lastInsert = i + 2;
    } else {
        int depth = 1;
        int j = i + 2;
        while (depth > 0) // WTF, just a for loop, but in a while-disguise
        {
            if (tokens[j] == "(")
                ++depth;
            if (tokens[j] == ")")
                --depth;
            j++;
        } 
        vector<string>::iterator it;
        it = tokens.begin() + j;
        tokens.insert(it, ")");
        lastInsert = j;
    }
    
    if (non)
    {
        // We should check if the first token after the the inserted ")" is this operator
        if (lastInsert + 1 < tokens.size() && tokens[lastInsert + 1] == op)
        {
            TException up(6, "The operator (" + op + ") is defined non-associative but is used otherwise");
            throw up;
        }
    }
}

bool isOperator(string &internal)
{
    Symbol s;
    try {
        s = findInternal(internal);
    } catch (TException e) {
        return false;
    }
    
    return s.symbolType == Symbol::Operator;
}

void processOperators_raw(vector<string> &tokens)
{
    // Implement operator precedence and function-rewrite
    OperatorMap::iterator it;
    
    // Check for undefined operators
    for (unsigned int i = 0; i < tokens.size(); i++)
        if (isOperator(tokens[i]) && operatorMap.find(tokens[i]) == operatorMap.end())
        {
            // Undefined operator found
            TException up(7, "The operator (" + tokens[i] + ") is not defined");
            throw up;
        }
    
    // Precedence
    unsigned int operatorsUsed = 0;
    
    for (unsigned int prec = 1; operatorsUsed < operatorMap.size(); prec++)
    {
        for (it = operatorMap.begin(); it != operatorMap.end(); it++)
        {
            Thinder::Operator op = (*it).second;
            if (op.precedence != prec)
                continue;
            
            operatorsUsed++;
            if (op.associativity == Thinder::Operator::Left)
            {
                for (unsigned int i = 0; i < tokens.size(); i++)
                    if (tokens[i] == op.op)
                        parenthesize(tokens, i++, false); // i++, because of the added '(' before this token
            } else if (op.associativity == Thinder::Operator::Right)
            {
                for (int i = tokens.size() - 1; i >= 0; i--)
                    if (tokens[i] == op.op)
                        parenthesize(tokens, i++, false);
            } else if (op.associativity == Thinder::Operator::Non)
            {
                // "a (+) b (+) c" is forbidden, you should use parentheses with non-associativity
                for (unsigned int i = 0; i < tokens.size(); i++)
                    if (tokens[i] == op.op)
                        parenthesize(tokens, i++, true);
            }
        }
    }
}


void processOperators(vector<string> &tokens)
{
    // Implement operator precedence and function-rewrite
    OperatorMap::iterator it;
        
    // Precedence
    unsigned int operatorsUsed = 0;
    
    for (unsigned int prec = 1; operatorsUsed < operatorMap.size(); prec++)
    {
        for (it = operatorMap.begin(); it != operatorMap.end(); it++)
        {
            Thinder::Operator op = (*it).second;
            if (op.precedence != prec)
                continue;
            
            operatorsUsed++;
            if (op.associativity == Thinder::Operator::Left)
            {
                for (unsigned int i = 0; i < tokens.size(); i++)
                {
                    string opstr;
                    try {
                        opstr = findSymbolName(findInternal(tokens[i]))[0].identifier;
                    } catch (TException e) {
                        continue;
                    }
                    
                    if (opstr == op.op)
                        parenthesize(tokens, i++, false); // i++, because of the added '(' before this token
                }
            } else if (op.associativity == Thinder::Operator::Right)
            {
                for (int i = tokens.size() - 1; i >= 0; i--)
                {
                    string opstr;
                    try {
                        opstr = findSymbolName(findInternal(tokens[i]))[0].identifier;
                    } catch (TException e) {
                        continue;
                    }
                    if (opstr == op.op)
                        parenthesize(tokens, i++, false);
                }
            } else if (op.associativity == Thinder::Operator::Non)
            {
                // "a (+) b (+) c" is forbidden, you should use parentheses with non-associativity
                for (unsigned int i = 0; i < tokens.size(); i++)
                {
                    string opstr;
                    try {
                        opstr = findSymbolName(findInternal(tokens[i]))[0].identifier;
                    } catch (TException e) {
                        continue;
                    }
                    if (opstr == op.op)
                        parenthesize(tokens, i++, true);
                }
            }
        }
    }
}


ParseTree::Type getTokenType(vector<string> tokens)
{
    std::cout << "getTokenType.tokens = ";
    for (unsigned int it = 0; it < tokens.size(); it++)
        std::cout << tokens[it] << " ";
    std::cout << std::endl;  
   // if (tokens.size() == 1) {
        string token = tokens[0];
        // internal runtime functions:
        if (token.find_first_of("t_int_op_") != string::npos)
        {
            if (token.find_first_of("int_int(") != string::npos)
            {
                // This token was an int operation
                ParseTree::Type type;
                type.typeType = ParseTree::Type::PrimitiveType;
                type.primitiveType = ParseTree::Type::Int;
                return type;
            }
        }     
        
        // internal cast functions:   
        if (token.find_first_of("t_int_int(") != string::npos)
        {
            // This token was an int literal
            ParseTree::Type type;
            type.typeType = ParseTree::Type::PrimitiveType;
            type.primitiveType = ParseTree::Type::Int;
            return type;
        } 
        if (token.find_first_of("t_int_string(") != string::npos)
        {
            // This token was a string literal
        } 
        if (token.find_first_of("t_int_char(") != string::npos)
        {
            // This token was a char literal
            ParseTree::Type type;
            type.typeType = ParseTree::Type::PrimitiveType;
            type.primitiveType = ParseTree::Type::Char;
            return type;
        } 
        if (token.find_first_of("t_int_float(") != string::npos)
        {
            // This token was a float literal
            ParseTree::Type type;
            type.typeType = ParseTree::Type::PrimitiveType;
            type.primitiveType = ParseTree::Type::Float;
            return type;
        } 
        
        // others:
        if (token.substr(token.length() - 1) == ")")
        {
            // Function call
            string internal = token.substr(0, token.find_first_of("("));
            return findInternal(internal).type;
        }
        if (token.find_first_of(".") != string::npos)
        {
            string internal = token.substr(token.find_last_of(".") + 1);
            return findInternal(internal).type;
        }
        
        return findInternal(token).type;
    //}
}

// TODO: rewrite
void expandOperators(vector<string> &tokens)
{
    //std::cout << "expandOperators.tokens = ";
    /*for (unsigned int it = 0; it < tokens.size(); it++)
        std::cout << tokens[it] << " ";
    std::cout << std::endl;  */
    
    for (unsigned int i = 0; i < tokens.size(); i++)
        if (isOperator(tokens[i]))
        {
            //std::cout << "found operator: " << tokens[i] << std::endl;
            unsigned int posLeft = i;
            int open = 1;
            while (posLeft > 0)
            {
                posLeft--;
                if (tokens[posLeft] == "(")
                    open--;
                if (tokens[posLeft] == ")")
                    open++;
                    
                if (open == 0)
                    break;
            }
            if (tokens[posLeft] == "(")
                posLeft++;
            
            open = 1;
            unsigned int posRight = i;
            while (posRight < tokens.size() - 1)
            {
                posRight++;
                if (tokens[posRight] == "(")
                    open++;
                if (tokens[posRight] == ")")
                    open--;
                    
                if (open == 0)
                    break;
            }
            if (tokens[posRight] == ")")
                posRight--;
                        
            vector<string> lhs;
            vector<string> rhs;
            vector<string> rewritten;
            lhs.insert(lhs.begin(), tokens.begin() + posLeft, tokens.begin() + i);
            rhs.insert(rhs.begin(), tokens.begin() + i + 1, tokens.begin() + posRight + 1);
            
            if (lhs.size() == 0 || rhs.size() == 0)
                continue;
                
            rewritten.push_back(tokens[i]);
            rewritten.push_back("(");
            rewritten.insert(rewritten.end(), lhs.begin(), lhs.end());
            rewritten.push_back(",");
            rewritten.insert(rewritten.end(), rhs.begin(), rhs.end());
            rewritten.push_back(")");
            
            
            /*std::cout << "expandOperators.lhs = ";
            for (unsigned int it = 0; it < lhs.size(); it++)
                std::cout << lhs[it] << " ";
            std::cout << std::endl;  
            
            std::cout << "expandOperators.rhs = ";
            for (unsigned int it = 0; it < rhs.size(); it++)
                std::cout << rhs[it] << " ";
            std::cout << std::endl;  */
            
            tokens.erase(tokens.begin() + posLeft, tokens.begin() + posRight + 1);
            tokens.insert(tokens.begin() + posLeft, rewritten.begin(), rewritten.end()); 
            i = posLeft + 1;
        }
    
    /*std::cout << "expandOperators.tokens (end) = ";
    for (unsigned int it = 0; it < tokens.size(); it++)
        std::cout << tokens[it] << " ";
    std::cout << std::endl;  */
}

string generateOpExpr(ParseTree::Name ns, ParseTree::OpExpr &oe, ParseTree::Type typeHint)
{
    string c = "";
    vector<string> tokens = generateOpExpr2(ns, oe, typeHint);
    
    processOperators(tokens);
    expandOperators(tokens);
    
    for (unsigned int i = 0; i < tokens.size(); i++)
    {
        c += tokens[i];
    }
    return c;
}

string generateUnary(ParseTree::Name ns, ParseTree::Unary &unary, ParseTree::Type typeHint)
{
    string s = "";
    for (unsigned int i = 0; i < unary.prefix.size(); i++)
    {
        s += unary.prefix[i].op + " ";
    }
    
    s += generatePrimary(ns, unary.primary, typeHint);
    for (unsigned int i = 0; i < unary.postfix.size(); i++)
    {
        s += " " + unary.postfix[i].op;
    }
    
    return s;
}

vector<ParseTree::IdentRest> primaryPostToIdentRest(ParseTree::PrimaryPost &pp)
{
    vector<ParseTree::IdentRest> rest;
    
    do
    {
        ParseTree::IdentRest r;
        if (pp.ppType == ParseTree::PrimaryPost::Ident)
        {
            r.identRestType = ParseTree::IdentRest::Ident;
            r.identifier = new ParseTree::Identifier(*pp.identifier);
            rest.push_back(r);
        }
        if (pp.ppType == ParseTree::PrimaryPost::Func)
        {
            r.identRestType = ParseTree::IdentRest::Func;
            r.funcArguments = pp.funcArguments;
            rest.push_back(r);
        }
        if (pp.ppType == ParseTree::PrimaryPost::Array)
        {
            r.identRestType = ParseTree::IdentRest::Array;
            r.arrayExpr = pp.arrayExpr;
            rest.push_back(r);
        }
        if (pp.next == NULL)
            break;
        pp = *pp.next;
    } while (pp.ppType != ParseTree::PrimaryPost::Empty);
    return rest;
}

ParseTree::Expression generateLiteralExpression(ParseTree::Literal::LiteralType literalType, string literal)
{
    ParseTree::Expression expr;
    
    expr.exprType = ParseTree::Expression::ExprOp;
    expr.opExpr = new ParseTree::OpExpr;
    expr.opExpr->opExprType = ParseTree::OpExpr::Single;
    expr.opExpr->unary = new ParseTree::Unary;
    expr.opExpr->unary->primary.primaryExpression.peType = ParseTree::PrimaryExpression::Literal;
    expr.opExpr->unary->primary.primaryExpression.literal = new ParseTree::Literal;
    expr.opExpr->unary->primary.primaryExpression.literal->literalType = literalType;
    expr.opExpr->unary->primary.primaryExpression.literal->literal = literal;
    expr.opExpr->unary->primary.primaryPost.ppType = ParseTree::PrimaryPost::Empty;
    
    return expr;
}

string generatePrimary(ParseTree::Name ns, ParseTree::Primary &primary, ParseTree::Type typeHint)
{
    string s = "";
    if (primary.primaryExpression.peType == ParseTree::PrimaryExpression::Literal)
    {
        // if there is a post, the literal is accessed as if it were an object
        // TODO if the typehint is an object, then also expand to an object
        // FIXME /\ HALF IMPLEMENTED 
        if (   primary.primaryPost.ppType != ParseTree::PrimaryPost::Empty
            || typeHint.typeType == ParseTree::Type::UserType
           )
        {
            // make it an object :-)
            ParseTree::Primary prim;
            prim.primaryExpression.peType = ParseTree::PrimaryExpression::New;
            prim.primaryPost = primary.primaryPost;
            if (primary.primaryExpression.literal->literalType == ParseTree::Literal::Int)
                prim.primaryExpression.name = toName("Thinder.Prim.Integer");
            prim.primaryExpression.arguments.push_back(generateLiteralExpression(primary.primaryExpression.literal->literalType, primary.primaryExpression.literal->literal));
            return generatePrimary(ns, prim, typeHint);
        }
                
        if (primary.primaryExpression.literal->literalType == ParseTree::Literal::Int)
            s += "t_int_int(" + primary.primaryExpression.literal->literal + ")";
        if (primary.primaryExpression.literal->literalType == ParseTree::Literal::Float)
            s += "t_int_float(" + primary.primaryExpression.literal->literal + ")";
        if (primary.primaryExpression.literal->literalType == ParseTree::Literal::String)
            s += "t_int_string(" + primary.primaryExpression.literal->literal + ")";
        if (primary.primaryExpression.literal->literalType == ParseTree::Literal::Char)
            s += "t_int_char(" + primary.primaryExpression.literal->literal + ")";
    } else if (primary.primaryExpression.peType == ParseTree::PrimaryExpression::Ident)
    {
        ParseTree::Designator d;
        d.identifier = primary.primaryExpression.identifier;
        d.rest = primaryPostToIdentRest(primary.primaryPost);
        s += generateDesignator(ns, d, typeHint);

    } else if (primary.primaryExpression.peType == ParseTree::PrimaryExpression::Expr)
    {
        s += generateExpression(ns, *primary.primaryExpression.expression, typeHint);
        // TODO: Expressions moeten ook een post kunnen hebben (zoals: "(i+5*3).Next()")
        if (primary.primaryPost.ppType != ParseTree::PrimaryPost::Empty)
        {
            TException up(17, "Expression post is not yet implemented");
            throw up;
        }
    } else if (primary.primaryExpression.peType == ParseTree::PrimaryExpression::New)
    {
        ParseTree::Name constructor = namespaceLookupFQN(ns, primary.primaryExpression.name);
        constructor.push_back(ParseTree::Identifier(constructor.back()));
        //std::cout << "constructor: " << printName(constructor) << std::endl;
        // find out what constructor we need
        ParseTree::Declaration declaration;
        declaration.declType = ParseTree::Declaration::FunctionDecl;
        declaration.type = symbolTable[constructor].type;
        //std::cout << "type: " << printType(declaration.type) << std::endl;
        vector<ParseTree::FormPar> argumentTypes;
        for (unsigned int i = 0; i < primary.primaryExpression.arguments.size(); i++)
        {
            ParseTree::FormPar fp;
            fp.type = getTypeOfExpression(ns, primary.primaryExpression.arguments[i]);
            //std::cout << "  argument: " << printType(fp.type) << std::endl;
            argumentTypes.push_back(fp);
        }
        declaration.formPars = argumentTypes;
        constructor = findOverload(ns, constructor, declaration);
        //std::cout << "constructor overload: " << printName(constructor) << std::endl;
        
        s += symbolTable[constructor].internal + "(";
        for (unsigned int i = 0; i < primary.primaryExpression.arguments.size(); i++)
        {
            //s += generateExpression(ns, primary.primaryExpression.arguments[i], typeHint);
            s += generateExpression(ns, primary.primaryExpression.arguments[i], symbolTable[constructor].argumentTypes[i]);
            if (i != primary.primaryExpression.arguments.size() - 1)
                s += ",";
        }
        s += ")";
        vector<ParseTree::IdentRest> rest = primaryPostToIdentRest(primary.primaryPost);
        Symbol sym = symbolTable[constructor];

        for(unsigned int i = 0; i < rest.size(); i++)             
            generateIdentRest(ns, sym, rest[i], s, i == rest.size() - 1, typeHint);
    }
    
    return s;
}

